package com.mytx.notify.filter;


import com.mytx.common.constant.Constant;
import com.mytx.common.exception.TokenException;
import com.mytx.common.utils.DateUtils;
import io.jsonwebtoken.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.concurrent.TimeUnit;


@SuppressWarnings("Duplicates")
public class JWTAuthenticationFilter extends BasicAuthenticationFilter {

    private static final Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class);

    private StringRedisTemplate redisTemplate;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager, StringRedisTemplate redisTemplate) {
        super(authenticationManager);
        this.redisTemplate = redisTemplate;
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String token = request.getHeader(DecodeFilter.AUTHORIZATION);

        if (StringUtils.isEmpty(token)) {
            chain.doFilter(request, response);
            return;
        }
        UsernamePasswordAuthenticationToken authentication = getAuthentication(request, response,token);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request, HttpServletResponse response,String token) {


        Claims claims;
        try {
           // logger.info("token:" + token);

            claims = Jwts.parser().setSigningKey("#$%$TFDGTRYU%^&#$%TERYHFRTY").parseClaimsJws(token).getBody();
        } catch (ExpiredJwtException e) {
            logger.error("Token已过期: {} " + e);
            throw new TokenException("Token已过期");
        } catch (UnsupportedJwtException e) {
            logger.error("Token格式错误: {} " + e);
            throw new TokenException("Token格式错误");
        } catch (MalformedJwtException e) {
            logger.error("Token没有被正确构造: {} " + e);
            throw new TokenException("Token没有被正确构造");
        } catch (SignatureException e) {
            logger.error("签名失败: {} " + e);
            throw new TokenException("签名失败");
        } catch (IllegalArgumentException e) {
            logger.error("非法参数异常: {} " + e);
            throw new TokenException("非法参数异常");
        }
        //logger.info("token解密：" + claims.toString());
        String orignalSalt = (String) claims.get("salt");
        String uId = (String) claims.get("u_id");
        Date currentDate = new Date();
        Date expiration = claims.getExpiration();
        //判断是否过期
        if (expiration.before(currentDate)) {
            logger.error("登录:Token已过期-" + uId);
            throw new TokenException("token无效");
        }

        String salt;
        try {
            salt = (String) redisTemplate.opsForHash().get(Constant.USER_SESSION + uId, "salt");

        } catch (Exception e) {
            logger.error("登录:" + e.getMessage());
            throw new TokenException("redis 缓存读取失败，请检查redis是否打开");
        }

        //sqlt匹配不上，说明用户已经在别的地方已经登录过了，需要重新登录
        if (!StringUtils.equals(salt, orignalSalt)) {
            logger.error("登录:salt不匹配-" + uId+"盐："+salt+"原始盐："+orignalSalt);
            throw new TokenException("token验证失败：salt不匹配");
        }

        // 判断token 是否在 7天内到期，如果是，更新token
        if (expiration.before(DateUtils.addDay(currentDate, 7))) {
            logger.info("token有效期在7天内，重建token:"+uId);
            Date expireDate = DateUtils.addDay(currentDate, Constant.LOGIN_EXPIRE_TIME);
            //生成token
            String newToken = Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(expireDate)
                    .signWith(SignatureAlgorithm.HS512, "#$%$TFDGTRYU%^&#$%TERYHFRTY")
                    .compact();
            //在响应中加入新token
            response.setHeader("token", newToken);
            //重新设置用户信息缓存有效期为30天
            redisTemplate.expire("USER_SESSION:" + uId, Constant.LOGIN_EXPIRE_TIME, TimeUnit.DAYS);

        }

        return new UsernamePasswordAuthenticationToken(uId, null, Collections.emptyList());

    }

}
